package com.imoresoft.agile.top;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class GenericUtils {

	public static String getInvokingMethodName() {
		StackTraceElement[] stacks = new Throwable().getStackTrace();
		return stacks[1].getMethodName();
	}

	@SuppressWarnings("unchecked")
	public static <T extends Object> T invoke(Object target, String methodName,
			Object[] args) throws NoSuchMethodException,
			IllegalArgumentException, IllegalAccessException,
			InvocationTargetException {
		int l = (args == null ? 0 : args.length);
		Class<?>[] argTypes = new Class<?>[l];
		for (int i = 0; i < l; i++) {
			argTypes[i] = args[i].getClass();
		}
		Class<?> clazz = target.getClass();
		Method method = clazz.getMethod(methodName, argTypes);
		if (method == null)
			return null;
		return (T) method.invoke(target, args);
	}

	public static Method findListGetMethodByReturnType(Class<?> clazz,
			Type parameterType) throws NoSuchMethodException {
		Method[] methods = clazz.getDeclaredMethods();
		for (Method m : methods) {
			Type t = m.getGenericReturnType();
			if (t instanceof ParameterizedType) {
				ParameterizedType pt = (ParameterizedType) t;
				Type[] rts = pt.getActualTypeArguments();
				if (rts.length == 1 && rts[0] == parameterType) {
					return m;
				}
			}
		}
		return null;
	}

	public static Method findListSetMethodByParameterType(Class<?> clazz,
			Type parameterType) throws NoSuchMethodException {
		Method[] methods = clazz.getDeclaredMethods();
		for (Method m : methods) {
			String methodName = m.getName();
			if (methodName.startsWith("set")) {
				Type[] ts = m.getGenericParameterTypes();
				if (ts != null && ts.length == 1) {
					if (ts[0] instanceof ParameterizedType) {
						ParameterizedType pt = (ParameterizedType) ts[0];
						Type[] rts = pt.getActualTypeArguments();
						if (rts.length == 1 && rts[0] == parameterType) {
							return m;
						}
					}
				}
			}
		}
		return null;
	}

	public static Method findGetMethodByReturnType(Class<?> clazz,
			Type returnType) throws NoSuchMethodException {
		Method[] methods = clazz.getDeclaredMethods();
		for (Method m : methods) {
			Type t = m.getGenericReturnType();
			if (t == returnType) {
				return m;
			}
		}
		return null;
	}

	public static Type getGenericParameterType(Class<?> clazz) {
		Type t = clazz.getGenericSuperclass();
		ParameterizedType pt = (ParameterizedType) t;
		return pt.getActualTypeArguments()[0];
	}
}
